package com.lambkit.core.http;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.lambkit.core.LambkitResult;
import com.lambkit.core.exception.LambkitException;
import com.lambkit.core.json.Json;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @author yangyong(孤竹行)
 */
public abstract class BaseHttpContext implements IHttpContext {

    private String urlPara;
    private String[] urlParaArray;

    private static final String[] NULL_URL_PARA_ARRAY = new String[0];
    private String urlParaSeparator = "-";

    public String getTarget() {
        return getRequest().getRequestURI();
    }
    /**
     * 获取 http 请求 body 中的原始数据，通常用于接收 json String 这类数据<br>
     * 可多次调用此方法，避免掉了 HttpKit.readData(...) 方式获取该数据时多次调用
     * 引发的异常
     * @return http 请求 body 中的原始数据
     */
    public String getRawData() {
        return getRequest().getRawData();
    }

    /**
     * Stores an attribute in this request
     * @param name a String specifying the name of the attribute
     * @param value the Object to be stored
     */
    public void setAttr(String name, Object value) {
        getRequest().setAttribute(name, value);
    }

    /**
     * Removes an attribute from this request
     * @param name a String specifying the name of the attribute to remove
     */
    public void removeAttr(String name) {
        getRequest().removeAttribute(name);
    }

    /**
     * Stores attributes in this request, key of the map as attribute name and value of the map as attribute value
     * @param attrMap key and value as attribute of the map to be stored
     */
    public void setAttrs(Map<String, Object> attrMap) {
        for (Map.Entry<String, Object> entry : attrMap.entrySet()) {
            getRequest().setAttribute(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Returns the value of a request parameter as a String, or null if the parameter does not exist.
     * <p>
     * You should only use this method when you are sure the parameter has only one value. If the
     * parameter might have more than one value, use getParaValues(java.lang.String).
     * <p>
     * If you use this method with a multivalued parameter, the value returned is equal to the first
     * value in the array returned by getParameterValues.
     * @param name a String specifying the name of the parameter
     * @return a String representing the single value of the parameter
     */
    public String getPara(String name) {
        return getRequest().getParameter(name);
    }

    /**
     * Returns the value of a request parameter as a String, or default value if the parameter does not exist.
     * @param name a String specifying the name of the parameter
     * @param defaultValue a String value be returned when the value of parameter is null
     * @return a String representing the single value of the parameter
     */
    public String getPara(String name, String defaultValue) {
        String result = getRequest().getParameter(name);
        return result != null && !"".equals(result) ? result : defaultValue;
    }

    /**
     * Returns the values of the request parameters as a Map.
     * @return a Map contains all the parameters name and value
     */
    public Map<String, String[]> getParaMap() {
        return getRequest().getParameterMap();
    }

    /**
     * Returns an Enumeration of String objects containing the names of the parameters
     * contained in this getRequest(). If the request has no parameters, the method returns
     * an empty Enumeration.
     * @return an Enumeration of String objects, each String containing the name of
     * 			a request parameter; or an empty Enumeration if the request has no parameters
     */
    public Enumeration<String> getParaNames() {
        return getRequest().getParameterNames();
    }

    /**
     * Returns an array of String objects containing all of the values the given request
     * parameter has, or null if the parameter does not exist. If the parameter has a
     * single value, the array has a length of 1.
     * @param name a String containing the name of the parameter whose value is requested
     * @return an array of String objects containing the parameter's values
     */
    public String[] getParaValues(String name) {
        return getRequest().getParameterValues(name);
    }

    /**
     * Returns an array of Integer objects containing all of the values the given request
     * parameter has, or null if the parameter does not exist. If the parameter has a
     * single value, the array has a length of 1.
     * @param name a String containing the name of the parameter whose value is requested
     * @return an array of Integer objects containing the parameter's values
     */
    public Integer[] getParaValuesToInt(String name) {
        String[] values = getRequest().getParameterValues(name);
        if (values == null || values.length == 0) {
            return null;
        }
        Integer[] result = new Integer[values.length];
        for (int i=0; i<result.length; i++) {
            result[i] = StrUtil.isBlank(values[i]) ? null : Integer.parseInt(values[i]);
        }
        return result;
    }

    public Long[] getParaValuesToLong(String name) {
        String[] values = getRequest().getParameterValues(name);
        if (values == null || values.length == 0) {
            return null;
        }
        Long[] result = new Long[values.length];
        for (int i=0; i<result.length; i++) {
            result[i] = StrUtil.isBlank(values[i]) ? null : Long.parseLong(values[i]);
        }
        return result;
    }

    /**
     * Returns an Enumeration containing the names of the attributes available to this getRequest().
     * This method returns an empty Enumeration if the request has no attributes available to it.
     * @return an Enumeration of strings containing the names of the request's attributes
     */
    public Enumeration<String> getAttrNames() {
        return getRequest().getAttributeNames();
    }

    /**
     * Returns the value of the named attribute as an Object, or null if no attribute of the given name exists.
     * @param name a String specifying the name of the attribute
     * @return an Object containing the value of the attribute, or null if the attribute does not exist
     */
    public <T> T getAttr(String name) {
        return (T)getRequest().getAttribute(name);
    }

    public <T> T getAttr(String name, T defaultValue) {
        T result = (T)getRequest().getAttribute(name);
        return result != null ? result : defaultValue;
    }

    /**
     * Returns the value of the named attribute as an Object, or null if no attribute of the given name exists.
     * @param name a String specifying the name of the attribute
     * @return an String Object containing the value of the attribute, or null if the attribute does not exist
     */
    public String getAttrToStr(String name) {
        return (String)getRequest().getAttribute(name);
    }

    /**
     * Returns the value of the named attribute as an Object, or null if no attribute of the given name exists.
     * @param name a String specifying the name of the attribute
     * @return an Integer Object containing the value of the attribute, or null if the attribute does not exist
     */
    public Integer getAttrToInt(String name) {
        return (Integer)getRequest().getAttribute(name);
    }

    /**
     * Returns the value of the specified request header as a String.
     */
    public String getHeader(String name) {
        return getRequest().getHeader(name);
    }

    private Integer toInt(String value, Integer defaultValue) {
        try {
            if (StrUtil.isBlank(value)) {
                return defaultValue;
            }
            value = value.trim();
            if (value.startsWith("N") || value.startsWith("n")) {
                return -Integer.parseInt(value.substring(1));
            }
            return Integer.parseInt(value);
        } catch (Exception e) {
            throw new LambkitException("Can not parse the parameter \"" + value + "\" to Integer value.");
            //throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),  "Can not parse the parameter \"" + value + "\" to Integer value.");
        }
    }

    /**
     * Returns the value of a request parameter and convert to Integer.
     * @param name a String specifying the name of the parameter
     * @return a Integer representing the single value of the parameter
     */
    public Integer getParaToInt(String name) {
        return toInt(getRequest().getParameter(name), null);
    }

    /**
     * Returns the value of a request parameter and convert to Integer with a default value if it is null.
     * @param name a String specifying the name of the parameter
     * @return a Integer representing the single value of the parameter
     */
    public Integer getParaToInt(String name, Integer defaultValue) {
        return toInt(getRequest().getParameter(name), defaultValue);
    }

    private Long toLong(String value, Long defaultValue) {
        try {
            if (StrUtil.isBlank(value)) {
                return defaultValue;
            }
            value = value.trim();
            if (value.startsWith("N") || value.startsWith("n")) {
                return -Long.parseLong(value.substring(1));
            }
            return Long.parseLong(value);
        }
        catch (Exception e) {
            throw new LambkitException("Can not parse the parameter \"" + value + "\" to Long value.");
            //throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),  "Can not parse the parameter \"" + value + "\" to Long value.");
        }
    }

    /**
     * Returns the value of a request parameter and convert to Long.
     * @param name a String specifying the name of the parameter
     * @return a Integer representing the single value of the parameter
     */
    public Long getParaToLong(String name) {
        return toLong(getRequest().getParameter(name), null);
    }

    /**
     * Returns the value of a request parameter and convert to Long with a default value if it is null.
     * @param name a String specifying the name of the parameter
     * @return a Integer representing the single value of the parameter
     */
    public Long getParaToLong(String name, Long defaultValue) {
        return toLong(getRequest().getParameter(name), defaultValue);
    }

    private Boolean toBoolean(String value, Boolean defaultValue) {
        if (StrUtil.isBlank(value)) {
            return defaultValue;
        }
        value = value.trim().toLowerCase();
        if ("1".equals(value) || "true".equals(value)) {
            return Boolean.TRUE;
        }
        else if ("0".equals(value) || "false".equals(value)) {
            return Boolean.FALSE;
        }
        throw new LambkitException("Can not parse the parameter \"" + value + "\" to Boolean value.");
        //throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),  "Can not parse the parameter \"" + value + "\" to Boolean value.");
    }

    /**
     * Returns the value of a request parameter and convert to Boolean.
     * @param name a String specifying the name of the parameter
     * @return true if the value of the parameter is "true" or "1", false if it is "false" or "0", null if parameter is not exists
     */
    public Boolean getParaToBoolean(String name) {
        return toBoolean(getRequest().getParameter(name), null);
    }

    /**
     * Returns the value of a request parameter and convert to Boolean with a default value if it is null.
     * @param name a String specifying the name of the parameter
     * @return true if the value of the parameter is "true" or "1", false if it is "false" or "0", default value if it is null
     */
    public Boolean getParaToBoolean(String name, Boolean defaultValue) {
        return toBoolean(getRequest().getParameter(name), defaultValue);
    }

    /**
     * Get all para from url and convert to Boolean
     */
//    public Boolean getParaToBoolean() {
//        return toBoolean(getPara(), null);
//    }
//
//    /**
//     * Get para from url and conver to Boolean. The first index is 0
//     */
//    public Boolean getParaToBoolean(int index) {
//        return toBoolean(getPara(index), null);
//    }
//
//    /**
//     * Get para from url and conver to Boolean with default value if it is null.
//     */
//    public Boolean getParaToBoolean(int index, Boolean defaultValue) {
//        return toBoolean(getPara(index), defaultValue);
//    }

    private Date toDate(String value, Date defaultValue) {
        try {
            if (StrUtil.isBlank(value)) {
                return defaultValue;
            }
            // return new java.text.SimpleDateFormat("yyyy-MM-dd").parse(value.trim());
            return Convert.toDate(value, defaultValue);
        } catch (Exception e) {
            throw new LambkitException("Can not parse the parameter \"" + value + "\" to Date value.");
            //throw new ActionException(400, renderManager.getRenderFactory().getErrorRender(400),  "Can not parse the parameter \"" + value + "\" to Date value.");
        }
    }

    /**
     * Returns the value of a request parameter and convert to Date.
     * @param name a String specifying the name of the parameter
     * @return a Date representing the single value of the parameter
     */
    public Date getParaToDate(String name) {
        return toDate(getRequest().getParameter(name), null);
    }

    /**
     * Returns the value of a request parameter and convert to Date with a default value if it is null.
     * @param name a String specifying the name of the parameter
     * @return a Date representing the single value of the parameter
     */
    public Date getParaToDate(String name, Date defaultValue) {
        return toDate(getRequest().getParameter(name), defaultValue);
    }

    /**
     * Return a Object from session.
     * @param key a String specifying the key of the Object stored in session
     */
    public <T> T getSessionAttr(String key) {
        ISession session = getRequest().getSession(false);
        return session != null ? (T)session.getAttribute(key) : null;
    }

    public <T> T getSessionAttr(String key, T defaultValue) {
        T result = getSessionAttr(key);
        return result != null ? result : defaultValue;
    }

    /**
     * Store Object to session.
     * @param key a String specifying the key of the Object stored in session
     * @param value a Object specifying the value stored in session
     */
    public void setSessionAttr(String key, Object value) {
        getRequest().getSession(true).setAttribute(key, value);
    }

    /**
     * Remove Object in session.
     * @param key a String specifying the key of the Object stored in session
     */
    public void removeSessionAttr(String key) {
        ISession session = getRequest().getSession(false);
        if (session != null) {
            session.removeAttribute(key);
        }
    }

    /**
     * Get cookie value by cookie name.
     */
    public String getCookie(String name, String defaultValue) {
        ICookie cookie = getCookieObject(name);
        return cookie != null ? cookie.getValue() : defaultValue;
    }

    /**
     * Get cookie value by cookie name.
     */
    public String getCookie(String name) {
        return getCookie(name, null);
    }

    /**
     * Get cookie value by cookie name and convert to Integer.
     */
    public Integer getCookieToInt(String name) {
        String result = getCookie(name);
        return result != null ? Integer.parseInt(result) : null;
    }

    /**
     * Get cookie value by cookie name and convert to Integer.
     */
    public Integer getCookieToInt(String name, Integer defaultValue) {
        String result = getCookie(name);
        return result != null ? Integer.parseInt(result) : defaultValue;
    }

    /**
     * Get cookie value by cookie name and convert to Long.
     */
    public Long getCookieToLong(String name) {
        String result = getCookie(name);
        return result != null ? Long.parseLong(result) : null;
    }

    /**
     * Get cookie value by cookie name and convert to Long.
     */
    public Long getCookieToLong(String name, Long defaultValue) {
        String result = getCookie(name);
        return result != null ? Long.parseLong(result) : defaultValue;
    }

    /**
     * Get cookie object by cookie name.
     */
    public ICookie getCookieObject(String name) {
        ICookie[] cookies = getRequest().getCookies();
        if (cookies != null) {
            for (ICookie cookie : cookies) {
                if (cookie.getName().equals(name)) {
                    return cookie;
                }
            }
        }
        return null;
    }

    /**
     * Get all cookie objects.
     */
    public ICookie[] getCookieObjects() {
        ICookie[] result = getRequest().getCookies();
        return result != null ? result : new ICookie[0];
    }

    /**
     * Set ICookie.
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     * @param isHttpOnly true if this cookie is to be marked as HttpOnly, false otherwise
     */
    public void setCookie(String name, String value, int maxAgeInSeconds, Boolean isHttpOnly) {
        doSetCookie(name, value, maxAgeInSeconds, null, null, isHttpOnly);
    }

    /**
     * Set ICookie.
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     */
    public void setCookie(String name, String value, int maxAgeInSeconds) {
        doSetCookie(name, value, maxAgeInSeconds, null, null, null);
    }

    /**
     * Set ICookie to getResponse().
     */
    public void setCookie(ICookie cookie) {
        getResponse().addCookie(cookie);
    }

    /**
     * Set ICookie to getResponse().
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     * @param path see ICookie.setPath(String)
     * @param isHttpOnly true if this cookie is to be marked as HttpOnly, false otherwise
     */
    public void setCookie(String name, String value, int maxAgeInSeconds, String path, Boolean isHttpOnly) {
        doSetCookie(name, value, maxAgeInSeconds, path, null, isHttpOnly);
    }

    /**
     * Set ICookie to getResponse().
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     * @param path see ICookie.setPath(String)
     */
    public void setCookie(String name, String value, int maxAgeInSeconds, String path) {
        doSetCookie(name, value, maxAgeInSeconds, path, null, null);
    }

    /**
     * Set ICookie to getResponse().
     * @param name cookie name
     * @param value cookie value
     * @param maxAgeInSeconds -1: clear cookie when close browser. 0: clear cookie immediately.  n>0 : max age in n seconds.
     * @param path see ICookie.setPath(String)
     * @param domain the domain name within which this cookie is visible; form is according to RFC 2109
     * @param isHttpOnly true if this cookie is to be marked as HttpOnly, false otherwise
     */
    public void setCookie(String name, String value, int maxAgeInSeconds, String path, String domain, Boolean isHttpOnly) {
        doSetCookie(name, value, maxAgeInSeconds, path, domain, isHttpOnly);
    }

    /**
     * Remove ICookie.
     */
    public void removeCookie(String name) {
        doSetCookie(name, null, 0, null, null, null);
    }

    /**
     * Remove ICookie.
     */
    public void removeCookie(String name, String path) {
        doSetCookie(name, null, 0, path, null, null);
    }

    /**
     * Remove ICookie.
     */
    public void removeCookie(String name, String path, String domain) {
        doSetCookie(name, null, 0, path, domain, null);
    }

    public abstract void doSetCookie(String name, String value, int maxAgeInSeconds, String path, String domain, Boolean isHttpOnly);

    /**
     * Keep all parameter's value except model value
     */
    public void keepPara() {
        Map<String, String[]> map = getRequest().getParameterMap();
        for (Map.Entry<String, String[]> e: map.entrySet()) {
            String[] values = e.getValue();
            if (values.length == 1) {
                getRequest().setAttribute(e.getKey(), values[0]);
            }
            else {
                getRequest().setAttribute(e.getKey(), values);
            }
        }
    }

    /**
     * Keep parameter's value names pointed, model value can not be kept
     */
    public void keepPara(String... names) {
        for (String name : names) {
            String[] values = getRequest().getParameterValues(name);
            if (values != null) {
                if (values.length == 1) {
                    getRequest().setAttribute(name, values[0]);
                }
                else {
                    getRequest().setAttribute(name, values);
                }
            }
        }
    }

    /**
     * Convert para to special type and keep it
     */
    public void keepPara(Class type, String name) {
        String[] values = getRequest().getParameterValues(name);
        if (values != null) {
            if (values.length == 1) {
                getRequest().setAttribute(name, Convert.convertQuietly(type, values[0]));
            }
            else {
                getRequest().setAttribute(name, values);
            }
        }
    }

    public void keepPara(Class type, String... names) {
        if (type == String.class) {
            keepPara(names);
        }
        if (names != null) {
            for (String name : names) {
                keepPara(type, name);
            }
        }
    }

    /**
     * Return true if the para value is blank otherwise return false
     */
    public boolean isParaBlank(String paraName) {
        return StrUtil.isBlank(getRequest().getParameter(paraName));
    }

    /**
     * Return true if the para exists otherwise return false
     */
    public boolean isParaExists(String paraName) {
        return getRequest().getParameterMap().containsKey(paraName);
    }

    /**
     * 为了进一步省代码，创建与 setAttr(...) 功能一模一样的缩短版本 set(...)
     */
    public void set(String attributeName, Object attributeValue) {
        getRequest().setAttribute(attributeName, attributeValue);
    }

    // --- 以下是为了省代码，为 getPara 系列方法创建的缩短版本

    public String get(String name) {
        return getPara(name);
    }

    public String get(String name, String defaultValue) {
        return getPara(name, defaultValue);
    }

    public Integer getInt(String name) {
        return getParaToInt(name);
    }

    public Integer getInt(String name, Integer defaultValue) {
        return getParaToInt(name, defaultValue);
    }

    public Long getLong(String name) {
        return getParaToLong(name);
    }

    public Long getLong(String name, Long defaultValue) {
        return getParaToLong(name, defaultValue);
    }

    public Boolean getBoolean(String name) {
        return getParaToBoolean(name);
    }

    public Boolean getBoolean(String name, Boolean defaultValue) {
        return getParaToBoolean(name, defaultValue);
    }

    public Date getDate(String name) {
        return getParaToDate(name);
    }

    public Date getDate(String name, Date defaultValue) {
        return getParaToDate(name, defaultValue);
    }

    // --- 以下是将自定义参数解析方法/action/para1-para2-para3形式

    public void setUrlPara(String urlPara) {
        this.urlPara = urlPara;
        this.urlParaArray = null;
    }

    public void setUrlPara(String urlPara, String separator) {
        this.urlPara = urlPara;
        this.urlParaArray = null;
        this.urlParaSeparator = separator;
    }

    public void setUrlParaSeparator(String separator) {
        this.urlParaSeparator = separator;
    }

    public String getUrlPara() {
        return urlPara;
    }

    public String getUrlParaSeparator() {
        return urlParaSeparator;
    }

    /**
     * Get all para with separator char from url
     */
    public String getPara() {
        if ("".equals(urlPara)) {// urlPara maybe is "" see ActionMapping.getAction(String)
            urlPara = null;
        }
        return urlPara;
    }

    /**
     * Get para from url. The index of first url para is 0.
     */
    public String getPara(int index) {
        if (index < 0) {
            return getPara();
        }
        if (urlParaArray == null) {
            if (urlPara == null || "".equals(urlPara)) {    // urlPara maybe is "" see ActionMapping.getAction(String)
                urlParaArray = NULL_URL_PARA_ARRAY;
            } else {
                urlParaArray = urlPara.split(urlParaSeparator);
            }
            for (int i=0; i<urlParaArray.length; i++) {
                if ("".equals(urlParaArray[i])) {
                    urlParaArray[i] = null;
                }
            }
        }
        return urlParaArray.length > index ? urlParaArray[index] : null;
    }

    /**
     * Get para from url with default value if it is null or "".
     */
    public String getPara(int index, String defaultValue) {
        String result = getPara(index);
        return result != null && !"".equals(result) ? result : defaultValue;
    }

    /**
     * Get para from url and conver to Integer. The first index is 0
     */
    public Integer getParaToInt(int index) {
        return toInt(getPara(index), null);
    }

    /**
     * Get para from url and conver to Integer with default value if it is null.
     */
    public Integer getParaToInt(int index, Integer defaultValue) {
        return toInt(getPara(index), defaultValue);
    }

    /**
     * Get para from url and conver to Long.
     */
    public Long getParaToLong(int index) {
        return toLong(getPara(index), null);
    }

    /**
     * Get para from url and conver to Long with default value if it is null.
     */
    public Long getParaToLong(int index, Long defaultValue) {
        return toLong(getPara(index), defaultValue);
    }

    /**
     * Get all para from url and convert to Integer
     */
    public Integer getParaToInt() {
        return toInt(getPara(), null);
    }

    /**
     * Get all para from url and convert to Long
     */
    public Long getParaToLong() {
        return toLong(getPara(), null);
    }

    /**
     * Get all para from url and convert to Boolean
     */
    public Boolean getParaToBoolean() {
        return toBoolean(getPara(), null);
    }

    /**
     * Get para from url and conver to Boolean. The first index is 0
     */
    public Boolean getParaToBoolean(int index) {
        return toBoolean(getPara(index), null);
    }

    /**
     * Get para from url and conver to Boolean with default value if it is null.
     */
    public Boolean getParaToBoolean(int index, Boolean defaultValue) {
        return toBoolean(getPara(index), defaultValue);
    }

    // --- 以下是 getPara 系列中获取 urlPara 的缩短版本

	/* 为了让继承类可以使用名为 get 的 action 注掉此方法，可使用 get(-1) 来实现本方法的功能
	public String get() {
		return getPara();
	} */

    public String get(int index) {
        return getPara(index);
    }

    public String get(int index, String defaultValue) {
        return getPara(index, defaultValue);
    }

    public Integer getInt() {
        return getParaToInt();
    }

    public Integer getInt(int index) {
        return getParaToInt(index);
    }

    public Integer getInt(int index, Integer defaultValue) {
        return getParaToInt(index, defaultValue);
    }

    public Long getLong() {
        return getParaToLong();
    }

    public Long getLong(int index) {
        return getParaToLong(index);
    }

    public Long getLong(int index, Long defaultValue) {
        return getParaToLong(index, defaultValue);
    }

    public Boolean getBoolean() {
        return getParaToBoolean();
    }

    public Boolean getBoolean(int index) {
        return getParaToBoolean(index);
    }

    public Boolean getBoolean(int index, Boolean defaultValue) {
        return getParaToBoolean(index, defaultValue);
    }

    public <T> T getBean(Class<T> beanClass) {
        String beanName = beanClass.getSimpleName();
        return (T)injectBean(beanClass, firstCharToLowerCase(beanName), getRequest(), false);
    }

    public <T> T getBean(Class<T> beanClass, boolean skipConvertError) {
        String beanName = beanClass.getSimpleName();
        return (T)injectBean(beanClass, firstCharToLowerCase(beanName), getRequest(), skipConvertError);
    }

    public <T> T getBean(Class<T> beanClass, String beanName) {
        return (T)injectBean(beanClass, beanName, getRequest(), false);
    }

    public <T> T getBean(Class<T> beanClass, String beanName, boolean skipConvertError) {
        return (T)injectBean(beanClass, beanName, getRequest(), skipConvertError);
    }

    private <T> T injectBean(Class<T> beanClass, String beanName, IRequest request, boolean skipConvertError) {
        Object bean = null;
        try {
            bean = beanClass.newInstance();
        } catch (Exception e) {
            if (skipConvertError == false) {
                throw new RuntimeException(e);
            }
        }
        String modelNameAndDot = StrUtil.isNotBlank(beanName) ? beanName + "." : null;
        Map<String, String[]> parasMap = request.getParameterMap();
        Method[] methods = beanClass.getMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith("set") == false || methodName.length() <= 3) {	// only setter method
                continue;
            }
            Class<?>[] types = method.getParameterTypes();
            if (types.length != 1) {						// only one parameter
                continue;
            }

            String attrName = firstCharToLowerCase(methodName.substring(3));
            String paraName = modelNameAndDot != null ? modelNameAndDot + attrName : attrName;
            if (parasMap.containsKey(paraName)) {
                try {
                    String paraValue = request.getParameter(paraName);
                    Object value = paraValue != null ? Convert.convertQuietly(types[0], paraValue) : null;
                    method.invoke(bean, value);
                } catch (Exception e) {
                    if (skipConvertError == false) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

        return (T)bean;
    }

    private String firstCharToLowerCase(String str) {
        char firstChar = str.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            char[] arr = str.toCharArray();
            arr[0] = (char)(arr[0] + 32);
            return new String(arr);
        } else {
            return str;
        }
    }

    // --- 文件参数获取方式
    private boolean isSafeFile(IUploadFile uploadFile) {
        String fileName = uploadFile.getFileName().trim().toLowerCase();
        if (fileName.endsWith(".jsp") || fileName.endsWith(".jspx")) {
            uploadFile.getFile().delete();
            return false;
        }
        return true;
    }

    public IUploadFile getFile(String name, String uploadPath) {
        return getFile(name, uploadPath, -1);
    }

    public IUploadFile getFile(String name, String uploadPath, long maxPostSize, String encoding) {
        return getFile(name, uploadPath, maxPostSize);
    }

    public boolean isPOST() {
        return getRequest().getMethod().equals("POST");
    }

    public boolean isGET() {
        return getRequest().getMethod().equals("GET");
    }

    public boolean isPut() {
        return getRequest().getMethod().equals("PUT");
    }

    public boolean isDelete() {
        return getRequest().getMethod().equals("DELETE");
    }

    @Override
    public void renderCaptcha() {

    }

    @Override
    public boolean validateCaptcha(String captchaParamName) {
        return false;
    }

    @Override
    public void renderHtml(String html) {

    }

    @Override
    public void render(IRender render) {
        if (render != null) {
            render.render(this);
        }
    }

    @Override
    public void renderError(int errorStatus, String msg) {
        getResponse().setHttpStatus(errorStatus, msg);
    }

    @Override
    public void renderJson(Object data) {
        IRender render = new JsonRender(data);
        render.render(this);
    }

    @Override
    public void renderJson(LambkitResult result) {
        if (result != null) {
            IRender render = new JsonRender(Json.use().toJson(result));
            render.render(this);
        }
    }

    /**
     * It creates the extra attribute below while tomcat take SSL open.
     * http://git.oschina.net/jfinal/jfinal/issues/10
     */
    protected static final Set<String> excludedAttrs = new HashSet<String>() {
        private static final long serialVersionUID = 9186138395157680676L;
        {
            add("javax.servlet.getRequest().ssl_session");
            add("javax.servlet.getRequest().ssl_session_id");
            add("javax.servlet.getRequest().ssl_session_mgr");
            add("javax.servlet.getRequest().key_size");
            add("javax.servlet.getRequest().cipher_suite");
            add("_res");	// I18nInterceptor 中使用的 _res
        }
    };

    public Map getRequestAttrs(String[] attrs) {
        Map map = new HashMap();
        if (attrs != null) {
            for (String key : attrs) {
                map.put(key, getRequest().getAttribute(key));
            }
        } else {
            for (Enumeration<String> attrsEnu=getRequest().getAttributeNames(); attrsEnu.hasMoreElements();) {
                String key = attrsEnu.nextElement();
                if (excludedAttrs.contains(key)) {
                    continue;
                }

                Object value = getRequest().getAttribute(key);
                map.put(key, value);
            }
        }
        return map;
    }

}
